﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using System.IO;
using System.Windows;
using System.Windows.Media;
using ThreeByte.ActiveDeck.AddIn.Content.UserResponse;
using log4net;
using ThreeByte.ActiveDeck.Config;
using ThreeByte.ActiveDeck.AddIn.Content.Controls;
using ThreeByte.Media;
using ThreeByte.ActiveDeck.AddIn.Config;
using ThreeByte.ActiveDeck.AddIn.Config.Properties;
using ThreeByte.ActiveDeck.Converters;

namespace ThreeByte.ActiveDeck.Content
{
    public enum Orientation { Portrait, LandscapeLeft, LandscapeRight, UpsideDown };

    public static class SlideUtil
    {

        private static readonly ILog log = LogManager.GetLogger(typeof(SlideUtil));

        static SlideUtil() {
            UserResponseNames = new HashSet<string>();
            UserResponseNames.Add(@"ActiveDeck_Chart");
            UserResponseNames.Add(@"ActiveDeck_Text");
            UserResponseNames.Add(@"ActiveDeck_Categories");
        }


        //TODO: handle things that are square?
        public static Orientation GetOrientation(int width, int height) {
            if(width < height) {
                return Orientation.Portrait;
            }
            return Orientation.LandscapeLeft;
        }

        //Scale some dimensions equally to match the desired resolution
        public static void ScaleTo(ref int width, ref int height, int xDim, int yDim) {
            double scaleX = (xDim / (double)width);
            double scaleY = (yDim / (double)height);

            width = (int)(width * Math.Min(scaleX, scaleY));
            height = (int)(height * Math.Min(scaleX, scaleY));
        }


        public static BitmapImage Snapshot(PowerPoint.SlideRange slides) {

            if (slides == null) {
                return null;
            }
            string filePath = System.IO.Path.GetTempFileName();
            //string filePath = string.Format(@"C:\Users\chris\Desktop\slideXport{0:000}.png", ++instance);
            int width = (int)(slides.Master.Width);
            int height = (int)(slides.Master.Height);
            Orientation orientation = GetOrientation(width, height);
            log.DebugFormat("Width: {0} Height: {1} Orientaiton: {2}", width, height, orientation);
            //Look at DPI?
            //TODO: Get rid of magic numbers
            if((orientation == Orientation.LandscapeLeft) || (orientation == Orientation.LandscapeRight)) {
                ScaleTo(ref width, ref height, 1024, 768);
            } else {
                ScaleTo(ref width, ref height, 768, 1024);
            }
            log.DebugFormat("Scaled Width: {0} Scaled Height: {1}", width, height);
            slides.Export(filePath, "PNG", width, height);

            //Read in the file into memory
            BitmapImage newImage = new BitmapImage();
            newImage.BeginInit();
            newImage.CacheOption = BitmapCacheOption.OnLoad;
            newImage.UriSource = new Uri(filePath);
            newImage.EndInit();

            //Delete the file
            File.Delete(filePath);

            return newImage;
        }

        public static BitmapImage GetPreview(PowerPoint.Slide slide) {

            //Get a temp file name
            string filePath = System.IO.Path.GetTempFileName();

            slide.Export(filePath, "PNG");
            // Force cursor to stop spinning after the COM call - #7332
            System.Windows.Input.Mouse.UpdateCursor();

            //Open that file and display it - Read the file into memory
            BitmapImage newImage = new BitmapImage();
            newImage.BeginInit();
            newImage.CacheOption = BitmapCacheOption.OnLoad;
            newImage.UriSource = new Uri(filePath);
            newImage.EndInit();

            //Delete the file
            System.IO.File.Delete(filePath);

            return newImage;
        }


        public static BitmapImage Snapshot(PowerPoint.Slide slide) {

            if(slide == null) {
                return null;
            }
            string filePath = System.IO.Path.GetTempFileName();
            //string filePath = string.Format(@"C:\Users\chris\Desktop\slideXport{0:000}.png", ++instance);

            slide.Export(filePath, "PNG", 1024, 768);

            //Read in the file into memory
            BitmapImage newImage = new BitmapImage();
            newImage.BeginInit();
            newImage.CacheOption = BitmapCacheOption.OnLoad;
            newImage.UriSource = new Uri(filePath);
            newImage.EndInit();

            //Delete the file
            File.Delete(filePath);

            return newImage;
        }

        private static string SurfaceToBitmapFile(FrameworkElement surface) {
            //Transform xform = surface.LayoutTransform;
            //surface.LayoutTransform = null;


            int width = (int)surface.Width;
            int height = (int)surface.Height;

            Size sSize = new Size(width, height);
            surface.Measure(sSize);
            surface.Arrange(new Rect(sSize));

            surface.UpdateLayout();

            RenderTargetBitmap renderBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
            renderBitmap.Render(surface);

            //string filePath = string.Format(@"C:\Users\chris\Desktop\slideXport{0:000}.png", ++instance);
            string filePath = System.IO.Path.GetTempFileName();

            //Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filename));

            using(FileStream fStream = new FileStream(filePath, FileMode.Create)) {
                PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
                pngEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
                pngEncoder.Save(fStream);
            }
            //BitmapImage newBitmap;
            //using(MemoryStream memStream = new MemoryStream()) {
            //    PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
            //    pngEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
            //    pngEncoder.Save(memStream);
                
            //    //reset to the top
            //    memStream.Position = 0;
            //    newBitmap = new BitmapImage();
            //    newBitmap.BeginInit();
            //    newBitmap.CacheOption = BitmapCacheOption.OnLoad;
            //    newBitmap.StreamSource = memStream;
            //    newBitmap.EndInit();
            //    newBitmap.Freeze();
            //}


            //surface.LayoutTransform = xform;

            //return newBitmap;
            return filePath;
        }

        public static BitmapImage SurfaceToBitmap(FrameworkElement surface) {
            //Transform xform = surface.LayoutTransform;
            //surface.LayoutTransform = null;


            int width = (int)surface.Width;
            int height = (int)surface.Height;

            Size sSize = new Size(width, height);
            surface.Measure(sSize);
            surface.Arrange(new Rect(sSize));

            surface.UpdateLayout();

            RenderTargetBitmap renderBitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
            renderBitmap.Render(surface);

            //string filePath = string.Format(@"C:\Users\chris\Desktop\slideXport{0:000}.png", ++instance);
            //string filePath = System.IO.Path.GetTempFileName();

            ////Directory.CreateDirectory(System.IO.Path.GetDirectoryName(filename));

            //using (FileStream fStream = new FileStream(filePath, FileMode.Create)) {
            //    PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
            //    pngEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
            //    pngEncoder.Save(fStream);
            //}
            BitmapImage newBitmap;
            using (MemoryStream memStream = new MemoryStream()) {
                PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
                pngEncoder.Frames.Add(BitmapFrame.Create(renderBitmap));
                pngEncoder.Save(memStream);

                //reset to the top
                memStream.Position = 0;
                newBitmap = new BitmapImage();
                newBitmap.BeginInit();
                newBitmap.CacheOption = BitmapCacheOption.OnLoad;
                newBitmap.StreamSource = memStream;
                newBitmap.EndInit();
                newBitmap.Freeze();
            }


            //surface.LayoutTransform = xform;

            return newBitmap;
            //return filePath;
        }

        public static bool ContainsUserResponseGraphic(PowerPoint.SlideRange slideRange) {
            foreach(PowerPoint.Shape s in slideRange.Shapes) {
                if(UserResponseNames.Contains(s.Name)) {
                    Console.WriteLine("Found it");
                    return true;
                }
            }

            return false;
        }

        private static readonly HashSet<string> UserResponseNames;

        public static List<PowerPoint.Shape> GetUserResponseGraphics(PowerPoint.Slide slide) {
            List<PowerPoint.Shape> shapes = new List<PowerPoint.Shape>();
            foreach (PowerPoint.Shape s in slide.Shapes) {
                if(UserResponseNames.Contains(s.Name)) {
                    shapes.Add(s);
                }
            }

            return shapes;
        }

        //private static PixelTranslator StatusBarTranslator = new PixelTranslator() { YOffset = -20 };
        private static LayoutOrientationToLengthConverter WidthConverter = new LayoutOrientationToLengthConverter() { DefaultLength = 768, RotatedLength = 1024 };
        private static LayoutOrientationToLengthConverter HeightConverter = new LayoutOrientationToLengthConverter() { DefaultLength = 1024, RotatedLength = 768 };

        public static List<DesignerControl> GetLayerElements(PowerPoint.Slide slide, Orientation orientation) {
            List<PowerPoint.Shape> sourceShapes = new List<PowerPoint.Shape>();
            return GetLayerElements(slide, ref sourceShapes,
                System.Convert.ToInt32(HeightConverter.Convert(orientation, typeof(int), null, null)),
                System.Convert.ToInt32(WidthConverter.Convert(orientation, typeof(int), null, null)));
        }
        public static List<DesignerControl> GetLayerElements(PowerPoint.Slide slide, ref List<PowerPoint.Shape> sourceShapes, int height = 1024, int width = 768) {

            List<DesignerControl> elements = new List<DesignerControl>();

            PixelScaler currentPixelScaler = new PixelScaler();
            currentPixelScaler.XScale = ((double)width / slide.Master.Width);
            currentPixelScaler.YScale = currentPixelScaler.XScale;

            bool foundOneVideo = false;
            foreach(Microsoft.Office.Interop.PowerPoint.Shape s in slide.Shapes) {
                PixelSpace pos = new PixelSpace() { H = (int)(s.Height), W = (int)(s.Width), X = (int)(s.Left), Y = (int)(s.Top) };
                pos = currentPixelScaler.Scale(pos);
                //pos = StatusBarTranslator.Translate(pos);

                string pageLink = s.Tags[PropertyTags.PAGE_LINK];
                string layerAction = s.Tags[PropertyTags.LAYER_ACTION];
                string backAction = s.Tags[PropertyTags.BACK_ACTION];
                string commandData = s.Tags[PropertyTags.DATA_COMMAND];
                string displayCondition = s.Tags[PropertyTags.DISPLAY_CONDITION];
                if(!(string.IsNullOrEmpty(pageLink))) {
                    int pageLinkNumber = int.Parse(pageLink);
                    //log.InfoFormat("Found an interactive element: {0} - [{1}]", s.Type, pageLinkNumber);

                    NavigationAction newAction = new NavigationAction() {
                        Jump = true,
                        Page = pageLinkNumber
                    };

                    try {
                        DesignerButton newInteractiveControl = (DesignerButton)GetButtonForShape(s, width, height, pos, "Back");
                        newInteractiveControl.Action = newAction;
                        elements.Add(newInteractiveControl);
                        sourceShapes.Add(s);

                        ////Create temporary file
                        //string filePath = System.IO.Path.GetTempFileName();

                        ////int width = 768;
                        ////int height = 1024;
                        //PowerPoint.PpExportMode mode = PowerPoint.PpExportMode.ppRelativeToSlide;

                        //s.Export(filePath, PowerPoint.PpShapeFormat.ppShapeFormatPNG, width, height, mode);
                        ////s.Export(filePath, Microsoft.Office.Interop.PowerPoint.PpShapeFormat.ppShapeFormatPNG);

                        //GraphicResource newLinkResource = new GraphicResource(string.Format("PageLinkButton_{0}", pageLinkNumber), new Uri(filePath), ImageType.PNG);

                        ////Create the Link Button
                        //DesignerControl newInteractiveControl = new DesignerButton(newLinkResource, pos) {
                        //    Action = new NavigationAction() {
                        //        Jump = true,
                        //        Page = pageLinkNumber
                        //    }
                        //};
                        //elements.Add(newInteractiveControl);
                        //sourceShapes.Add(s);

                        ////Delete the file
                        //File.Delete(filePath);
                    } catch(Exception ex) {
                        log.Error("Cannot export shape type", ex);
                    }
                } else if(!(string.IsNullOrEmpty(layerAction))) {
                    //Parse the Layer Action
                    NavigationAction newAction = ParseLayerAction(layerAction);

                    try {
                        DesignerButton newInteractiveControl = (DesignerButton)GetButtonForShape(s, width, height, pos, "Back");
                        newInteractiveControl.Action = newAction;
                        elements.Add(newInteractiveControl);
                        sourceShapes.Add(s);

                        ////Create temporary file
                        //string filePath = System.IO.Path.GetTempFileName();

                        ////int width = 768;
                        ////int height = 1024;
                        //PowerPoint.PpExportMode mode = PowerPoint.PpExportMode.ppRelativeToSlide;

                        //s.Export(filePath, PowerPoint.PpShapeFormat.ppShapeFormatPNG, width, height, mode);

                        //GraphicResource newLinkResource = new GraphicResource(string.Format("LayerAction_{0}", newAction.State), new Uri(filePath), ImageType.PNG);

                        ////Create the Link Button
                        //DesignerControl newInteractiveControl = new DesignerButton(newLinkResource, pos) {
                        //    Action = newAction
                        //};
                        //elements.Add(newInteractiveControl);
                        //sourceShapes.Add(s);

                        ////Delete the file
                        //File.Delete(filePath);
                    } catch(Exception ex) {
                        log.Error("Cannot export shape type", ex);
                    }

                } else if(!(string.IsNullOrEmpty(commandData))) {
                    //Parse the Command Data
                    NavigationAction newAction = new NavigationAction() { Command = true };
                    newAction.CommandData = commandData;

                    try {
                        DesignerButton newInteractiveControl = (DesignerButton)GetButtonForShape(s, width, height, pos, "Command");
                        newInteractiveControl.Action = newAction;
                        elements.Add(newInteractiveControl);
                        sourceShapes.Add(s);

                        ////Create temporary file
                        //string filePath = System.IO.Path.GetTempFileName();

                        ////int width = 768;
                        ////int height = 1024;
                        //PowerPoint.PpExportMode mode = PowerPoint.PpExportMode.ppRelativeToSlide;

                        //s.Export(filePath, PowerPoint.PpShapeFormat.ppShapeFormatPNG, width, height, mode);

                        //GraphicResource newLinkResource = new GraphicResource(string.Format("CommandAction_{0}", newAction.State), new Uri(filePath), ImageType.PNG);

                        ////Create the Link Button
                        //DesignerControl newInteractiveControl = new DesignerButton(newLinkResource, pos) {
                        //    Action = newAction
                        //};
                        //elements.Add(newInteractiveControl);
                        //sourceShapes.Add(s);

                        ////Delete the file
                        //File.Delete(filePath);
                    } catch(Exception ex) {
                        log.Error("Cannot export shape type", ex);
                    }

                } else if(!(string.IsNullOrEmpty(backAction))) {
                    try {
                        DesignerButton newInteractiveControl = (DesignerButton)GetButtonForShape(s, width, height, pos, "Back");
                        newInteractiveControl.Action = new NavigationAction() { Back = true };;
                        elements.Add(newInteractiveControl);
                        sourceShapes.Add(s);
                    } catch(Exception ex) {
                        log.Error("Cannot export shape type", ex);
                    }

                } else if(s.Type == Microsoft.Office.Core.MsoShapeType.msoMedia) {
                    //Look for videos

                    
                    if(!foundOneVideo) {
                        foundOneVideo = true; //Only allow one video per page

                        //Look for a Link type media element
                        try {
                            string pathRoot = ThisAddIn.CurrentAddIn.GetCurrentConfig().GetPresentationPath();
                            string mediaPath = s.LinkFormat.SourceFullName;
                            DesignerVideo newVideo;
                            string resourceId = s.Tags[PropertyTags.VIDEO_RESOURCE_ID];
                            if(!string.IsNullOrEmpty(resourceId)) {
                                //Construct the VideoControl
                                newVideo = new DesignerVideo(Guid.Parse(resourceId), mediaPath, pathRoot, pos);
                            } else {
                                newVideo = new DesignerVideo(mediaPath, pathRoot, pos);
                                s.Tags.Add(PropertyTags.VIDEO_RESOURCE_ID, newVideo.GetMediaResources().Single().ID.ToString());
                            }
                            newVideo.LayoutOrientation = Orientation.LandscapeLeft;
                            elements.Add(newVideo);
                            sourceShapes.Add(s);

                        } catch(Exception ex) {
                            log.Error("Cannot export video type", ex);
                        }
                    } else {
                        log.WarnFormat("Only one video is allowed per page");
                        MessageBox.Show("You can only have one video per Layer.  Only the first video will be transfered.", "Video Limit");
                    }
                } else {
                    //Just export as a static image
                    try {
                        //Create temporary file
                        string filePath = System.IO.Path.GetTempFileName();

                        //int width = 768;
                        //int height = 1024;
                        PowerPoint.PpExportMode mode = PowerPoint.PpExportMode.ppRelativeToSlide;

                        s.Export(filePath, PowerPoint.PpShapeFormat.ppShapeFormatPNG, width, height, mode);
                        //s.Export(filePath, Microsoft.Office.Interop.PowerPoint.PpShapeFormat.ppShapeFormatPNG);

                        GraphicResource newLinkResource = new GraphicResource("LayerGraphic", new Uri(filePath), ImageType.PNG);

                        //Create the Link Button
                        DesignerControl newInteractiveControl = new DesignerImage(newLinkResource, pos) { Fill = true, Condition = displayCondition };
                        elements.Add(newInteractiveControl);
                        sourceShapes.Add(s);

                        //Delete the file
                        File.Delete(filePath);
                    } catch(Exception ex) {
                        log.Error("Cannot export shape type", ex);
                    }
                }
            }

            return elements;
        }

        private static DesignerControl GetButtonForShape(Microsoft.Office.Interop.PowerPoint.Shape shape, int slideWidth, int slideHeight, PixelSpace pos, string name = "") {
            //Create temporary file
            string filePath = System.IO.Path.GetTempFileName();
            shape.Export(filePath, PowerPoint.PpShapeFormat.ppShapeFormatPNG, slideWidth, slideHeight, PowerPoint.PpExportMode.ppRelativeToSlide);
            GraphicResource newLinkResource = new GraphicResource(name, new Uri(filePath), ImageType.PNG);
            File.Delete(filePath);
            string displayCondition = shape.Tags[PropertyTags.DISPLAY_CONDITION];

            return new DesignerButton(newLinkResource, pos) { Condition = displayCondition };
        }

        public static HashSet<PowerPoint.Shape> GetInteractiveShapes(PowerPoint.Slide slide) {
            HashSet<PowerPoint.Shape> shapes = new HashSet<PowerPoint.Shape>();

            foreach(PowerPoint.Shape shape in slide.Shapes) {
                foreach(string tag in PropertyTags.All) {
                    if(!(string.IsNullOrEmpty(shape.Tags[tag]))) {
                        shapes.Add(shape);
                    }
                }
            }
            return shapes;
        }


        public static NavigationAction ParseLayerAction(string layerAction) {
            NavigationAction newAction = new NavigationAction() { Layer = true };

            int splitPoint = layerAction.IndexOf(':');
            string state = layerAction.Substring(0, splitPoint);
            //Parse into an enum
            EnabledState targetState = (EnabledState)(Enum.Parse(typeof(EnabledState), state));
            splitPoint++;

            string target = layerAction.Substring(splitPoint, layerAction.Length - splitPoint);
            newAction.LayerTarget = int.Parse(target);
            newAction.State = targetState;
            return newAction;
        }

        public static List<int> GetSlideIdsInPresentationOrder(PowerPoint.Presentation pres){
            List<int> slideIds = new List<int>();

            List<PowerPoint.Slide> slides = new List<PowerPoint.Slide>();


            foreach(PowerPoint.Slide s in pres.Slides){
                slides.Add(s);
            }

            foreach(PowerPoint.Slide s in slides.OrderBy(p => p.SlideNumber)) {
                slideIds.Add(s.SlideID);
            }

            return slideIds;
        }

    }
}
